package klock

import (
	"hash/fnv"
	"sync"
)

type Klock struct {
	locks []*sync.RWMutex
	size  uint32
}

func NewKlock(size uint32) *Klock {
	var locks = make([]*sync.RWMutex, size)
	for i := range locks {
		locks[i] = &sync.RWMutex{}
	}
	return &Klock{
		locks: locks,
		size:  size,
	}
}

func (k *Klock) Lock(key string) {
	k.getLock(key).Lock()
}

func (k *Klock) Unlock(key string) {
	k.getLock(key).Unlock()
}

func (k *Klock) TryLock(key string) bool {
	return k.getLock(key).TryLock()
}

func (k *Klock) RLock(key string) {
	k.getLock(key).RLock()
}

func (k *Klock) RUnlock(key string) {
	k.getLock(key).RUnlock()
}

func (k *Klock) TryRLock(key string) bool {
	return k.getLock(key).TryRLock()
}

func (k *Klock) getLock(key string) *sync.RWMutex {
	hash := k.hash(key)
	return k.locks[hash%k.size]
}

func (k *Klock) hash(key string) uint32 {
	f := fnv.New32a()
	_, _ = f.Write([]byte(key))
	return f.Sum32()
}
